package com.ydlclass;

import com.ydlclass.ChannelHandler.handler.InboundHandler.MethodCallHandler;
import com.ydlclass.ChannelHandler.handler.InboundHandler.ProtocolFrameDecoder;
import com.ydlclass.ChannelHandler.handler.InboundHandler.YrpcMessageRequestDecoderHandler;
import com.ydlclass.ChannelHandler.handler.OutboundHandler.YrpcMessageResponseEncoderHandler;
import com.ydlclass.Transport.message.Request.YrpcRequest;
import com.ydlclass.annotation.YrpcApi;
import com.ydlclass.config.Configuration;
import com.ydlclass.core.HeartbeatDetector;
import com.ydlclass.loadbalancer.LoadBalance;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 启动引导程序
 * @Author duWenJian
 * @Date 2024/2/10 16:41
 * @Version 1.0
 */
@Slf4j
public class YrpcBootstrap {
    /**
     * ------------------服务提供方的相关api--------------------
     */
    public static final YrpcBootstrap yrpcBootstrap = new YrpcBootstrap();

    // 全局的配置中心
    public Configuration configuration;

    // 保存request可以到当前线程随时获取
    public static final ThreadLocal<YrpcRequest> REQUEST_THREAD_LOCAL = new ThreadLocal<>();


    // 连接的缓存，如果使用InetSocketAddress这样的类来作为key，一定要看看它的equals方法，因为InetSocketAddress的equals方法是根据ip和端口来判断的
    // 看有没有重写toString
    /**
     * 连接的缓存，如果使用InetSockAddress这样的类作为key，
     */
    public static final Map<InetSocketAddress,Channel> CHANNEL_CACHE = new ConcurrentHashMap<>(16);
    /**
     * 响应时间
     */
    public static final TreeMap<Long,Channel> ANSWER_TIME_CHANNEL_CACHE = new TreeMap<>();

    /**
     * 维护一个映射关系
     * 维护一个已经发布的并且暴露出去的服务列表， key -> interface的全限定名. value -> ServiceConfig<?>注册的服务
     * <接口的名字,具体的实现>
     */
    public static final Map<String,ServiceConfig<?>> SERVERS_LIST = new ConcurrentHashMap<>(16);


    /**
     * 定义全局的对外挂起的CompletableFuture
     */
    public static final Map<Long,CompletableFuture<Object>> PENDING_REQUEST = new ConcurrentHashMap<>(128);



    public YrpcBootstrap() {
        // 构造启动引导程序时，需要做一些什么事
        configuration = new Configuration();
    }

    /**
     * YrpcBootstrap 是一个单例，我们希望每一个应用程序只有一个实例；
     * @return 使用饿汉式来实现单例
     */
    public static YrpcBootstrap getInstance() {
        return yrpcBootstrap;
    }

    /**
     * 这个方法用来定义当前应用的名字
     * @param appName 应用的名称
     * @return this当前实例
     */
    public YrpcBootstrap application(String appName) {
        configuration.setAppName(appName);
        return this;
    }

    /**
     * 用来配置一个注册中心
     * @param registryConfig 注册中心
     * @return this当前实例
     */
    public YrpcBootstrap registry(RegistryConfig registryConfig) {
        // todo
        // 我们在这里维护一个zookeeper实例，但是如果这样写的话就会将zookeeper和当前工程耦合
        // 我们其实更希望以后可以扩展更多种不同的实现
        configuration.setRegistryConfig(registryConfig);
        return this;
    }

    /**
     * 用来配置负载均衡策略
     * @param loadBalance 负载均衡接口
     * @return this当前实例
     */
    public YrpcBootstrap LoadBalance(LoadBalance loadBalance) {
        configuration.setLoadBalance(loadBalance);
        return this;
    }

    /**
     * ------------------服务提供方的相关api--------------------
     */


    /**
     * 发布任务的方式
     * @param service 封装发布的实例
     * @return this当前实例
     */
    public YrpcBootstrap publish(ServiceConfig<?> service) {
        // 我们抽象了注册中心的概念，使用注册中心的一个实现来完成注册
        // 有人回想，现在难道不是强耦合吗?
        configuration.getRegistryConfig().getRegistry().register(service);

        // 1、当服务调用方通过接口、方法名、具体的方法参数列表发起调用的时候，提供方怎么知道使用的哪一个实现
        // (1)、new 一个具体事例  (2)、使用spring里面的beanFactory来完成.getBean()方法来获取bean  (3)、维护一个映射关系来获取
        SERVERS_LIST.put(service.getInterface().getName(),service);

        return this;
    }

    /**
     * 批量发布任务的形式，将接口->匹配的实现  注册到服务中心
     * @param ServiceList 需要批量发布的服务列表
     * @return this当前实例
     */
    public YrpcBootstrap publish(List<ServiceConfig<?>> ServiceList){
        ServiceList.forEach(service -> {
            configuration.getRegistryConfig().getRegistry().register(service);
        });
        return this;
    }
    /**
     * 启动netty服务
     */
    public void start() {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            ChannelFuture channelFuture = serverBootstrap.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        // 基于长度字段的帧解码器
                        ch.pipeline().addLast(new ProtocolFrameDecoder());
                        // Request解码器
                        ch.pipeline().addLast(new YrpcMessageRequestDecoderHandler());
                        // 调用具体实例、封装结果
                        ch.pipeline().addLast(new MethodCallHandler());
                        // Response编码器
                        ch.pipeline().addLast(new YrpcMessageResponseEncoderHandler());
                    }
                })
                .bind(configuration.getPort()).sync();
            Channel channel = channelFuture.channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    /**
     * ------------------服务调用方的相关api--------------------
     */

    public YrpcBootstrap reference(ReferenceConfig<?> reference) {
        // 开启对这个服务的心跳检测
        HeartbeatDetector.detectHeartbeat(reference.getInterface().getName());
        // 在这个方法里面我们是否可以拿到相关的配置项-注册中心
        // 配置reference，将来调用get的时候，方便生成代理对象
        reference.setRegistry(configuration.getRegistryConfig().getRegistry());
        reference.setGroup(this.getConfiguration().getGroup());
        return this;
    }

    /**
     * 配置序列化的方式
     * @param serializeType
     */
    public YrpcBootstrap serialize(String serializeType) {
        log.debug("当前工程使用了:{}进行序列化的协议", serializeType);
        configuration.setSerializerType(serializeType);
        return this;
    }

    /**
     * 配置解压缩的方式
     * @param gzip
     * @return
     */
    public YrpcBootstrap compress(String gzip) {
        log.debug("当前工程使用了:{}进行解压缩的协议", gzip);
        configuration.setCompressionType(gzip);
        return this;
    }

    /**
     * 扫描并发布
     * @param packageName
     * @return
     */
    public YrpcBootstrap scan(String packageName) {
        // 需要通过packageName获取旗下的所有的类的全限定名称
        List<String> classNames = getAllClassName(packageName);
        // 通过反射获得到所有的类，class对象
        List<? extends Class<?>> classList = classNames.stream()
                .map(className -> {
                            try {
                                // 通过路径反射到具体的class对象
                                return Class.forName(className);
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                        }
                )
                .filter(clazz -> clazz.getAnnotation(YrpcApi.class) != null)
                .collect(Collectors.toList());
        for (Class<?> aClass : classList) {
            Class<?>[] interfaces = aClass.getInterfaces();
            Object instance = null;
            try {
                instance = aClass.getConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
            YrpcApi annotation = aClass.getAnnotation(YrpcApi.class);
            String group = annotation.group();
            for (Class<?> anInterface : interfaces) {
                ServiceConfig<?> serviceConfig = new ServiceConfig<>();
                serviceConfig.setInterface(anInterface);
                serviceConfig.setRef(instance);
                serviceConfig.setGroup(group);
                // 发布服务
                if (log.isDebugEnabled()) {
                    log.debug("已经通过包扫描将服务{}发布",anInterface);
                }
                publish(serviceConfig);
            }
        }
        return this;
    }

    /**
     * 根据包名，获取该包下大发布的服务
     * @param packageName
     * @return
     */
    private List<String> getAllClassName(String packageName) {
        // 将java下的com.ydlclass  转化为  windows文件系统的 com/ydlclass
        String s = packageName.replaceAll("\\.", "/");
        // 看这个路径在绝对路径的那里
        // file:/M:/RPC/dwjclass/dwjRPC/dwjrpc-framework/dwjrpc-core/target/classes/com/ydlclass
        URL url = ClassLoader.getSystemClassLoader().getResource(s);
        // /M:/RPC/dwjclass/dwjRPC/dwjrpc-framework/dwjrpc-core/target/classes/com/ydlclass
        String path = url.getPath();

        List<String> classNames = new ArrayList<>();
        classNames = recursionFile(path,classNames,s);

        return classNames;
    }

    /**
     * 递归处理文件
     * @param path
     * @param className
     * @return
     */
    private List<String> recursionFile(String path, List<String> className,String basePath) {
        File file = new File(path);
        if (file.isDirectory()) {
            File[] files = file.listFiles(pathname -> pathname.isDirectory() || pathname.getPath().contains(".class"));
            if (files == null || files.length == 0){
                return className;
            }
            for (File f : files) {
                if (f.isDirectory()) {
                    // 递归调用
                    recursionFile(f.getAbsolutePath(), className, basePath);
                } else {
                   String AbsolutePathName = getClassNameByAbsolutePath(f.getAbsolutePath(),basePath);
                   className.add(AbsolutePathName);
                }
            }
        } else {
            // 文件 --> 类的全限定名
            if (file.getName().endsWith(".class")){
                String AbsolutePathName = getClassNameByAbsolutePath(file.getAbsolutePath(),basePath);
                className.add(AbsolutePathName);
            }
        }
        return className;
    }

    /**
     * 将绝对路径 M:\RPC\dwjclass\dwjRPC\dwjrpc-framework\dwjrpc-core\target\classes\com\ydlclass\YrpcBootstrap.class
     * 转化为    com\ydlclass\YrpcBootstrap.class
     * 再转化为  com.ydlclass.YrpcBootstrap.class
     * @param absolutePath
     * @return
     */
    private String getClassNameByAbsolutePath(String absolutePath , String basePath) {
        String fileName = absolutePath.substring(absolutePath.indexOf(basePath.replaceAll("/","\\\\")));
        fileName = fileName.replaceAll("\\\\", ".");
        String substring = fileName.substring(0, fileName.indexOf(".class"));
        return substring;
    }

    public static void main(String[] args) {
        List<String> allClassName = YrpcBootstrap.getInstance().getAllClassName("com.ydlclass");
        System.out.println(111);
        System.out.println(allClassName);
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public YrpcBootstrap group(String master) {
        this.getConfiguration().setGroup(master);
        return this;
    }
}
